home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / programming / basic / udpchat / old_stuff / udp_chatv1.7.asc < prev    next >
Encoding:
Text File  |  1999-05-14  |  20.2 KB  |  650 lines

  1. ;
  2. ;                      UDP Chat code V1.7   29/1/98
  3. ;
  4. ;    This code sends and receives UDP data packets like a
  5. ;  a simple IRC client, and  checks wether they have arrived
  6. ;  at their destination.
  7. ;
  8. ;  Written by Anton Reinauer <anton@ww.co.nz>.
  9. ;  GUI: Alvaro Thompson <alvaro@enterprise.net> - And awful hacks
  10. ;  by me to his nice font sensitive code :-)
  11. ;        - typical bloody games programmer ;-)
  12. ;
  13. ;  Thanks to Paul Burkey for TCP_Funcs and to Dr. Ercole Spiteri
  14. ;  for TCP-to-Blitz.
  15. ;
  16. ;  Turn overflow errors off in Debugger options
  17.  
  18.  
  19. WBStartup
  20. NoCli
  21. Hostname.s="localhost"  ; default destination host address
  22. #PORT=3001              ; default destination port to send data to
  23. #LOCALPORT=3001
  24. #NO_CONNECTION=1        ; are we connected, or have we been connected to the Internet?
  25.                         ; 1 for not been connected, 0 for have/are connected.
  26. GAME_NAME.s="UDP_Test"
  27.  
  28. INCLUDE "TCPFuncs.bb"
  29. INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
  30. DEFTYPE .w
  31.  
  32. packet_number.l=0
  33. online=0
  34. my_number=0
  35. ;********************************************************
  36.  
  37. NEWTYPE .message_status
  38.   number.l     ; packet number
  39.   ack.w        ; has it been received yet?
  40.   timestamp.l  ; when was it sent?
  41.   message.s    ; message
  42.   resends.w    ; how many times has it been re-sent?
  43.   player       ; which player was it sent too?
  44. End NEWTYPE
  45.  
  46. NEWTYPE .player_info
  47. status.w
  48. ascii_host_string.s
  49. End NEWTYPE
  50.  
  51. ;*******************************************************
  52. .Dims
  53.  
  54. Dim messages.message_status(100)
  55. Dim host.sockaddrin(8), hostlen.l(8)
  56. Dim players.player_info(8)
  57.  
  58. DEFTYPE .sockaddrin temphost
  59. ;*******************************************************
  60.  
  61. .Print_String
  62. Statement Print_String{text$}
  63.   SHARED ypos
  64.   If ypos<200
  65.     ypos+10
  66.   Else
  67.     WScroll 10,60,590,220,0,10
  68.   EndIf
  69.  
  70.   WLocate 10,ypos
  71.   Print text$               ; print string received
  72. End Function
  73.  
  74. .Get_Packet_Source
  75. Function.w Get_Packet_Source{}
  76.  SHARED host(),temphost
  77.  i=0
  78.  Repeat
  79.    i+1
  80.    If host(i)\sin_addr\s_addr=temphost\sin_addr\s_addr AND  host(i)\sin_port=temphost\sin_port
  81.      exit=1
  82.    EndIf
  83.    If i=8 AND exit=0
  84.      i=-1
  85.      exit=1
  86.    EndIf
  87.  Until  exit=1
  88.  
  89.  Function Return i
  90. End Function
  91.  
  92. .WriteUDP
  93. Statement WriteUDP{ad.l,size.w,player}
  94.   SHARED sock.l,host(),hostlen()
  95.  
  96.   ; This routine writes data via UDP
  97.  
  98.   sockwrite.l=0                         ;Clear Writemask
  99.   sockwrite.l BitSet sock.l             ;set Writemask on our socket
  100.   g=WaitSelect_(2,0,&sockwrite.l,0,0,0) ;Wait until server is ready to read our data
  101.   c=sendto_(sock.l,ad,size,0,host(player),hostlen(player))    ;Send data to server
  102.  
  103. End Statement
  104.  
  105. .Send_String_Message
  106. Statement Send_String_Message{send_string.s,player}
  107.  SHARED packet_number,messages(),free_message,last_message_number,REL_STRING_END.s
  108.  
  109.   send$=REL_STRING_END.s + Mkl$(packet_number) + send_string.s + Chr$(0)
  110.                                                                                                                               un
  111.   messages(free_message)\number=packet_number,False,Ticks,send$,0,player
  112.   If messages(last_message_number)\ack=True
  113.     last_message_number=free_message
  114.   EndIf
  115.  
  116.   WriteUDP{&send$,Len(send$),player}
  117.   packet_number+1
  118.   free_message+1
  119.   If free_message=101 Then free_message=0
  120.  
  121. End Statement
  122.  
  123. .ReadUDP
  124. Function .s ReadUDP{}
  125.   SHARED sock.l,TCPmem.l,temphost,temphostlen
  126.  
  127.   ; This Function reads data from the socket it is bound to.
  128.   ; If there is no messages then it will return an empty string =""
  129.  
  130.   sockread.l=0                              ;Clear Readmask
  131.   sockread.l BitSet sock.l                  ;Set Readmask on our socket
  132.   e=IoctlSocket_(sock.l,#FIONREAD,TCPmem.l) ;How much data is there?
  133.   f.l=Peek.l(TCPmem.l)                      ;Place value in f
  134.  
  135.   If f>0                                    ; any data waiting
  136.     temphostlen=SizeOf .sockaddrin
  137.     c=recvfrom_(sock.l,TCPmem.l,f,0,temphost,temphostlen)  ;Read all data waiting on socket
  138.                                                            ;and get host it was sent from in temphost
  139.     If c>0    ; if there any data waiting
  140.         a=0 : c$=""
  141.         Repeat
  142.             c$=c$+Chr$(Peek.b(TCPmem+a)) : a+1    ; build string
  143.         Until a=c
  144.     EndIf
  145.  
  146.   EndIf
  147.  
  148.   Function Return c$
  149. End Function
  150.  
  151. .Get_Host
  152. Function Get_Host{host$,port.w,player}
  153.   SHARED host(),hostlen()
  154.  
  155.   *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  156.   If *a.hostent=0
  157.     WLocate 10,18
  158.     Print "    No connection to host           "
  159.     Function Return False
  160.   Else
  161.     WLocate 10,18
  162.     Print "Type in data, and hit return to send"
  163.   EndIf
  164.  
  165.   ;Copy Details to our Sockaddrin structure
  166.  
  167.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length)
  168.  
  169.   host(player)\sin_port=port       ;Set port number
  170.   host(player)\sin_family=2        ;Set type to AT_INET
  171.   hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  172.  
  173.   Function Return True
  174. End Function
  175.  
  176. .Localhost_Name
  177. Function.s Localhost_Name{}
  178.     If OpenFile (0,"ENV:HOSTNAME")
  179.        FileInput 0
  180.        While NOT Eof(0)
  181.          a$=a$+Inkey$
  182.        Wend
  183.        CloseFile 0
  184.        WindowInput 0
  185.        Function Return a$
  186.     EndIf
  187.     Function Return "localhost"
  188. End Function
  189.  
  190. .ConnectUDP
  191. Function ConnectUDP{host$,port.w}
  192.   SHARED sock.l,host(),hostlen(),port_used,bsd_library_address.l
  193.   ;
  194.   ; Open UDP socket and bind it to a port number
  195.   ; Return true or False if successfull or not
  196.   ;
  197.  
  198.   bsd_library_address.l=OpenLibrary_("bsdsocket.library",0)
  199.   If bsd_library_address=0
  200.     Function Return False
  201.   Else
  202.       sock.l=socket_(2,2,0)        ; open UDP socket
  203.       If sock=-1
  204.         Function Return False
  205.       Else
  206.           *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  207.           If *a.hostent=0
  208.              Function Return False
  209.           EndIf
  210.                                                                                                                       inY=WBHeight
  211.           ;Copy Details to our Sockaddrin structure
  212.  
  213.           bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(0)\sin_addr,*a.hostent\h_length)
  214.  
  215.           host(0)\sin_port=port       ;Set port number
  216.           host(0)\sin_family=2        ;Set type to AT_INET
  217.           hostlen(0)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  218.  
  219.           temp=0
  220.           port_used=port
  221.           Repeat
  222.             host(0)\sin_port=port_used  ; for testing on localhost- keep trying one port at a time after #LOCALPORT for
  223.             If bind_(sock,host(0),hostlen(0))=0    ; bind socket to port so we can                                     free port
  224.               Function Return True           ; receive data on port
  225.             Else
  226.               port_used+1
  227.               temp+1
  228.             EndIf
  229.           Until temp=10
  230.       EndIf
  231.   EndIf
  232. End Function
  233.  
  234. .Resend_Message
  235. Function Resend_Message{message_number}
  236.   SHARED messages()
  237.   messages(message_number)\timestamp=Ticks
  238.   messages(message_number)\resends+1
  239.   If messages(message_number)\resends>5  ; is host not responding?
  240.      Print_String{"Host not responding to Packet no: " + Str$(message_number)}
  241.      Function Return False
  242.   Else
  243.      WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
  244.      Print_String{"Resent Packet no:" + Str$(message_number)}
  245.      Function Return True
  246.   EndIf
  247. End Statement
  248.  
  249. .Find_Next_Message
  250. Statement Find_Next_Message{}
  251. SHARED last_message_number,messages(),free_message
  252.  
  253.   exit=0
  254.   Repeat              ; find next unacknowledged message in array
  255.  
  256.     last_message_number+1
  257.     If last_message_number=101 Then last_message_number=0
  258.  
  259.     If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
  260.       If last_message_number=0
  261.         last_message_number=100
  262.         exit=1
  263.       Else
  264.         last_message_number-1
  265.         exit=1
  266.       EndIf
  267.     Else
  268.       If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
  269.         exit=1
  270.       EndIf
  271.     EndIf
  272.  
  273.   Until exit=1
  274. End Statement
  275.  
  276. .Acknowledge_Packet   ; mark message as been sucessfully sent
  277. Statement Acknowledge_Packet{ack_packet_number}
  278.   SHARED last_message_number,free_message,messages()
  279.  
  280.   a=last_message_number
  281.   exit=0
  282.   Repeat
  283.     If messages(a)\number=ack_packet_number   ; find message in sent messages array
  284.       messages(a)\ack=True         ; note it as being received
  285.       Format "0000"
  286.       current_time.l=Ticks
  287.       current_lag=current_time-messages(a)\timestamp ; calculate lag
  288.       b$=Str$(current_lag)
  289.       WLocate 142,32
  290.       Print b$         ; print lag onscreen
  291.       Format""
  292.       exit=1
  293.     EndIf
  294.     a+1
  295.     If a=101 Then a=0    ; if at end of array, jump to beginning
  296.   Until exit=1
  297.  
  298.   If a=last_message_number+1 OR (a=0 AND last_message_number=100)   ; if last message unacknowledged is acknowledged
  299.     Find_Next_Message{}                                             ; then find next unacknowledged message
  300.   Else                ; we have lost a packet- resend last packet number!
  301.     If messages(last_message_number)\timestamp-current_time > current_lag+5    ; if its behind the current lag resend it
  302.       Print_String{"Received packet ACK out of order, resending last unacknowledged packet."}
  303.       If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  304.         messages(last_message_number)\timestamp=current_time+500  ; stop it resending the packet infinitely
  305.         Acknowledge_Packet{last_message_number}       ; wipe message off message acknowledge array
  306.       EndIf
  307.     EndIf
  308.   EndIf
  309. End Statement
  310.  
  311. .Requested_Connection
  312. Function.s Requested_Connection{incoming_string.s}
  313.   SHARED sock,players(),host(),hostlen(),GAME_NAME,temphost,temphostlen,online,CP_REP_ACCEPT.s,CP_REP_REJECT.s
  314.  
  315.   If online<2
  316.     dummy=3
  317.     Repeat            ; build game name string from incoming request
  318.       b$=Mid$(incoming_string,dummy,1)
  319.       If b$<>Chr$(0)
  320.         c$=c$+b$
  321.       EndIf
  322.       dummy+1
  323.     Until b$=Chr$(0) OR dummy=(Len(incoming_string))
  324.  
  325.     If b$=Chr$(0)
  326.       If c$=GAME_NAME.s            ; if request is for correct game
  327.         protocol=Asc(Right$(incoming_string,1))
  328.         If protocol=#NET_PROTOCOL_VERSION  ; check wether using the same protocol
  329.           a$="Connection request from "
  330.           dummy=1
  331.           exit=0
  332.           Repeat      ;check if a spare player slot is availiable
  333.             dummy+1
  334.             If players(dummy)\status=0
  335.               exit=1
  336.             Else
  337.               If dummy=8
  338.                 exit=1
  339.                 dummy=-1
  340.               EndIf
  341.             EndIf
  342.           Until exit=1
  343.  
  344.           If dummy>0     ; connection accepted
  345.  
  346.             players(dummy)\status=1
  347.             *a.hostent=gethostbyaddr_(&temphost\sin_addr\s_addr,4,2)  ; check wether host exists- we're not being shammed
  348.  
  349.             If *a.hostent=0
  350.               Function Return Str$(temphost\sin_addr\s_addr) + "Failed"
  351.             EndIf
  352.  
  353.             bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(dummy)\sin_addr,*a.hostent\h_length) ; copy details to player's host
  354.                                                                                                 ; newtype, host()
  355.             host(dummy)\sin_port=temphost\sin_port       ;Set port number
  356.             host(dummy)\sin_family=2        ;Set type to AT_INET
  357.             hostlen(dummy)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  358.  
  359.             string_address.l=Inet_NtoA_(host(dummy)\sin_addr\s_addr) ; get ASCII string version of host address (a.b.c.d)
  360.             Repeat                       ; build ASCII host string
  361.               letter.b=(Peek.b(string_address))
  362.               If letter<>0
  363.                 temp$=temp$+Chr$(letter)
  364.               EndIf
  365.               string_address+1
  366.             Until letter=0
  367.             a$=a$+temp$ + "  Port: " + Str$(host(dummy)\sin_port)
  368.             players(dummy)\ascii_host_string=temp$   ; store it
  369.  
  370.             send$=CP_REP_ACCEPT.s + Mki$(host(0)\sin_port)+Chr$(dummy)  ; send connection accepted back
  371.             WriteUDP{&send$,Len(send$),dummy}
  372.             online=1
  373.             Function Return a$
  374.           Else
  375.             a$=a$+"- no spare player slot!"
  376.           EndIf
  377.         Else
  378.           a$="Connection request- Incorrect Protocol: "+Str$(protocol)
  379.         EndIf
  380.       Else
  381.         a$="Wrong game name connection request: "+b$
  382.       EndIf
  383.     Else
  384.       a$="Connection request- Unknown error!"
  385.     EndIf
  386.   Else
  387.     a$="Connection Request rejected- host already logged on as Client"
  388.   EndIf
  389.  
  390.   send$=CP_REP_REJECT.s + a$  ; send connection rejected back with reason
  391.   sockwrite.l=0                         ;Clear Writemask
  392.   sockwrite.l BitSet sock.l             ;set Writemask on our socket
  393.   g=WaitSelect_(2,0,&sockwrite.l,0,0,0) ;Wait until server is ready to read our data
  394.   c=sendto_(sock.l,&send$,Len(send$),0,temphost,temphostlen)    ;Send data to server
  395.  
  396.   Function Return a$
  397. End Function
  398.  
  399. .Decode_Packet
  400. Statement Decode_Packet{incoming_string.s}
  401.   SHARED players(),host(),GAME_NAME.s,temphost,temphostlen,online,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
  402.   packet_type=Asc(Left$(incoming_string,1))
  403.   packet_not_connected=0   ; check to see if packet should be from reliable online source
  404.   player=Get_Packet_Source{}  ; check to see if packet is from reliable online source
  405.  
  406.   Select packet_type
  407.     Case  #CONTROL_PACKET
  408.       packet_type=Asc(Mid$(incoming_string,2))
  409.       Select packet_type
  410.         Case #CP_REQ_CONNECT
  411.           a$=Requested_Connection{incoming_string}
  412.           packet_not_connected=1
  413.         Case #CP_REP_ACCEPT
  414.           online=2
  415.           my_number=Val(Left$(incoming_string,1))
  416.           players(1)\status=1
  417.           a$="Connection Accepted"
  418.         Case #CP_REP_REJECT
  419.           a$=Mid$(incoming_string,3)
  420.        End Select
  421.  
  422.     Case #REL_STRING_END
  423.       num$=Mid$(incoming_string,2,4)
  424.       packet_number.l=Cvl(num$)
  425.       a$=Str$(packet_number) + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
  426.       send$=REL_PACKET_ACK.s + Mkl$(packet_number)
  427.       WriteUDP{&send$,Len(send$),player}  ; send packet acknowledgement back
  428.  
  429.     Case #REL_PACKET_ACK
  430.       packet_number.l=Cvl(Mid$(incoming_string,2,4))
  431.       a$=": Packet no. " + Str$(packet_number) + " arrived at destination."
  432.       Acknowledge_Packet{packet_number}
  433.  
  434.   End Select
  435.  
  436.   If player<0 AND packet_not_connected=0
  437.     Print_String{"Warning: Packet from unknown source!"}
  438.     Statement Return
  439.   EndIf
  440.  
  441.   a$="R "+a$
  442.   Print_String{a$}
  443. End Statement
  444.  
  445. ;****************************************
  446.  
  447.  
  448. Gosub Init_Gui
  449.  
  450. ypos=40
  451. xpos=10
  452. free_message=0
  453. messages(free_message)\ack=True
  454. last_message_ack=0
  455.  
  456. CNIF #NO_CONNECTION=1    ; if not connected to the `net
  457.  
  458.   If ConnectUDP {Hostname,#LOCALPORT}              ; bind socket to local port
  459.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  460.      WLocate 510,4
  461.      Print "Bound to: ",port_used
  462.   Else
  463.      Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
  464.      Goto Exit
  465.   EndIf
  466.  
  467.   If Get_Host {Hostname,#PORT,0}  :EndIf
  468.  
  469. CELSE
  470.   If ConnectUDP {Localhost_Name{},#LOCALPORT}              ; bind socket to local port
  471.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  472.      WLocate 510,4
  473.      Print "Bound to: ",port_used
  474.   Else
  475.      Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
  476.      Goto Exit
  477.   EndIf
  478.  
  479.   If Get_Host {Localhost_Name{},#PORT,0}  :EndIf
  480. CEND
  481.  
  482. ResetTimer
  483.  
  484. ;****************************************
  485.  
  486. .Main
  487.  
  488. Repeat
  489.  
  490.     VWait
  491.  
  492.     If ev.l=$40
  493.       Select GadgetHit
  494.         Case51           ; Server to connect to
  495.           If online=0
  496.             If Get_Host{GTGetString(0,51), GTGetInteger(0,52),1}=True
  497.               Print_String{"Attempting to connect to Server"}
  498.               send$=CP_REQ_CONNECT.s+GAME_NAME.s+Chr$(0)+NET_PROTOCOL_VERSION.s
  499.               WriteUDP{&send$,Len(send$),1}
  500.             EndIf
  501.           Else
  502.             If online=2
  503.               Print_String{"Already logged on to a Server!"}
  504.             Else
  505.               Print_String{"Already acting as a Server!"}
  506.             EndIf
  507.           EndIf
  508.         Case52           ; Port to connect at above Server
  509.           If Get_Host{GTGetString(0,51), GTGetInteger(0,52),1} :EndIf
  510.         Case53           ; Delay 1s
  511.           VWait 50
  512.         Case54           ; get name of localhost
  513.           a$=Localhost_Name {}   ;
  514.           WLocate 315+pingwidth+15,hostnamey+3
  515.           Print a$
  516.         Case63       ; Delay 5s
  517.           VWait 2000   ; delay 40s
  518.         Case64         ; Send message to connected players
  519.          If online>0
  520.              send$=GTGetString(0,64)
  521.              Print_String{"S " +Str$(packet_number) +": "+ send$}
  522.              For i=1 To 8
  523.               If players(i)\status=1  AND i<>my_number
  524.                 Send_String_Message{send$,i}   ; Send string to connected players
  525.               EndIf
  526.              Next
  527.          EndIf
  528.        End Select
  529.     EndIf
  530.  
  531.     a$=ReadUDP{}   ; get data from socket
  532.  
  533.     If a$<>""
  534.        Decode_Packet{a$}
  535.     EndIf
  536.  
  537.     If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
  538.       If Ticks>messages(last_message_number)\timestamp+250  ; has it been 5 secs since the packet was sent?
  539.         If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  540.           Acknowledge_Packet{message_number}            ; wipe message off message acknowledge array
  541.         EndIf
  542.       EndIf
  543.     EndIf
  544.  
  545.     ev=Event
  546.  
  547. Until ev=$200    ; shut down if close gadget hit
  548.  
  549. CloseTCP{}                          ; close connection
  550.  
  551. dummy.l=CloseLibrary_(bsd_library_address)  ; close bsdsocket.library
  552.                                        ; or else Miami can't exit
  553. Exit:
  554. WLocate 10,18
  555. Print "                                    "
  556. WLocate 10,18
  557. Print"Socket closed"
  558. Delay_(30)
  559.  
  560. FreeMem TCPmem,$2000
  561.  
  562. End
  563.  
  564. ;****************************************
  565.  
  566. .Init_Gui
  567.     WBenchToFront_
  568.     WbToScreen0
  569.     DefaultIDCMP $20|$40|$200
  570.     *scr.Screen=Peek.l(Addr Screen(0))
  571.     *myfont.TextAttr=*scr\Font
  572.     fontheight=*myfont\ta_YSize
  573.     ad1.l=*myfont\ta_Name
  574.     fontname$=Peek$(ad1)
  575.  
  576.     LoadFont 1,fontname$,fontheight,0
  577.  
  578.     portwidth=TextLength_(*scr\_RastPort,"Port:",5)+10
  579.     numberswidth=TextLength_(*scr\_RastPort,"99999",5)+14
  580.     serverwidth=TextLength_(*scr\_RastPort,"Send To:",8)+10
  581.     connectwidth=TextLength_(*scr\_RastPort,"Connect",7)+14
  582.     disconnectwidth=TextLength_(*scr\_RastPort,"Disconnect",10)+14
  583.     pingwidth=TextLength_(*scr\_RastPort,"Localhost:",11)
  584.     delaywidth=TextLength_(*scr\_RastPort,"Delay 1s",8)+10
  585.     inputwidth=TextLength_(*scr\_RastPort,"Input:",6)+10
  586.     firstperson=TextLength_(*scr\_RastPort,"1:",2)+10
  587.  
  588.     #SERVERBTN       =51
  589.     #PORTBTN         =52
  590.     #BUTTON          =53
  591.  
  592.     x=1
  593.     y=1
  594.  
  595.     x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5
  596.  
  597.     If #NO_CONNECTION=0
  598.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
  599.     Else
  600.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
  601.     EndIf
  602.     GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
  603.     GTButton  0,#BUTTON,345,y,delaywidth,fontheight+4,"Delay 1s",$10
  604.     GTButton  0,63,340+delaywidth+10,y,delaywidth,fontheight+4,"Delay 5s",$10
  605.     y+fontheight+5
  606.  
  607.     hostnamey=y
  608.  
  609.     GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10
  610.  
  611.     WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90
  612.  
  613.     y+fontheight+5
  614.  
  615.     GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
  616.     GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
  617.     GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
  618.     GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
  619.     GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
  620.     GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
  621.     GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
  622.     GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
  623.  
  624.     GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""
  625.  
  626.     WinHeight=y+fontheight+5
  627.  
  628.     ;WinX=WBWidth/2-(WinWidth/2)
  629.     ;WinY=WBHeight/2-(WinHeight/2)
  630.  
  631.     WinTitle$="UDP Send"
  632.     ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."
  633.  
  634.     Window 0,0,10,WinWidth,245,$0002|$0004|$0008,WinTitle$,1,2
  635.     Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off
  636.  
  637.     WindowOutput0
  638.     WindowInput 0
  639.  
  640.     a$=Localhost_Name {}   ;
  641.     WLocate 315+pingwidth+15,hostnamey+3
  642.     Print a$
  643.  
  644.     WLocate 10,y+3
  645.     Print"Ping Time-VBL"
  646.  
  647. Return
  648.  
  649.  
  650.